package com.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.enums.BizCodeEnum;
import com.exception.BizException;
import com.interceptor.LoginInterceptor;
import com.mapper.ProjectMapper;
import com.model.ProjectDO;
import com.request.FileCreateRequest;
import com.request.FileDeleteRequest;
import com.request.FileRenameRequest;
import com.request.FileSaveRequest;
import com.service.FileManageService;
import com.utils.FileMappingUtil;
import com.utils.JsonData;
import com.vo.DirectoryOrFileVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author rkc
 * @date 2022/8/20 20:06
 */
@Slf4j
@Service
public class FileManageServiceImpl implements FileManageService {

    @Autowired
    private FileMappingUtil fileMappingUtil;

    @Resource
    private ProjectMapper projectMapper;

    /**
     * 列出当前用户命名空间下所有的文件、目录、子文件、子目录
     * @param name
     * @return
     */
    @Override
    public JsonData showDirectory(String name) {
        String path = fileMappingUtil.mapping(name);
        DirectoryOrFileVO dof = new DirectoryOrFileVO(path, name, null);
        getAllDirectory(dof, new File(path), path.length() - name.length() - 1);
        dof.setPath(name);
        return JsonData.buildSuccess(dof);
    }

    private void getAllDirectory(DirectoryOrFileVO dof, File f, int start) {
        File[] fs = f.listFiles();
        //目录在前文件在后，同是目录或者同是文件按照字典序排列
        fsort(fs);
        if (null != fs) {
            if (dof.getChildren() == null) {
                dof.setChildren(new ArrayList<>());
            }
            List<DirectoryOrFileVO> children = dof.getChildren();
            for (File fi : fs) {
                String path = fi.getAbsolutePath().substring(start);
                DirectoryOrFileVO dir = new DirectoryOrFileVO(path, fi.getName(), null);
                children.add(dir);
                if (fi.isDirectory()) {
                    getAllDirectory(dir, fi, start);
                }
            }
        }
    }

    private void fsort(File[] fs) {
        Arrays.sort(fs, (o1, o2) -> {
            if (o1.isDirectory() && o2.isFile()) {
                return -1;
            }
            if (o1.isFile() && o2.isDirectory()) {
                return 1;
            }
            String name1 = o1.getName(), name2 = o2.getName();
            return name1.compareTo(name2);
        });
    }

    /**
     * 根据请求信息创建对应文件
     * TODO：新增文件创建后的自填充模板
     * @param fileCreateRequest
     * @return
     */
    @Override
    public JsonData createFile(FileCreateRequest fileCreateRequest) {
        String path = fileMappingUtil.mapping(fileCreateRequest.getPath());
        File f = new File(path);
        boolean res;
        try {
            res = f.createNewFile();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new BizException(BizCodeEnum.FILE_HAS_EXISTED);
        }
        return res ? JsonData.buildSuccess() : JsonData.buildError("文件创建失败");
    }

    /**
     * 根据请求信息创建目录，且不能多级创建
     * @param fileCreateRequest
     * @return
     */
    @Override
    public JsonData createDirectory(FileCreateRequest fileCreateRequest) {
        File f = new File(fileMappingUtil.mapping(fileCreateRequest.getPath()));
        return f.mkdir() ? JsonData.buildSuccess() : JsonData.buildError("文件夹创建失败");
    }

    /**
     * 读取指定文件的内容
     * @param path 文件虚拟路径
     * @return
     */
    @Override
    public JsonData getFile(String path) {
        File f = new File(fileMappingUtil.mapping(path));
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append('\n');
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new BizException(BizCodeEnum.FILE_ERROR_OCCURRED);
        }
        return JsonData.buildSuccess(sb.toString());
    }

    /**
     * 保存文件
     * @return
     */
    @Override
    public JsonData saveFile(FileSaveRequest fileSaveRequest) {
        //将文本内容写入到文件中
        File f = new File(fileMappingUtil.mapping(fileSaveRequest.getPath()));
        if (!f.exists()) {
            log.warn("{}文件不存在", f.getAbsolutePath());
            throw new BizException(BizCodeEnum.FILE_NOT_EXISTED);
        }
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)))) {
            writer.write(fileSaveRequest.getContent());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new BizException(BizCodeEnum.FILE_ERROR_OCCURRED);
        }
        return JsonData.buildSuccess();
    }

    /**
     * 删除文件/目录，如果是目录，则递归删除
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData deleteAll(FileDeleteRequest fileDeleteRequest) {
        String path = fileMappingUtil.mapping(fileDeleteRequest.getPath());
        projectMapper.delete(new QueryWrapper<ProjectDO>().eq("uid", LoginInterceptor.THREAD_LOCAL.get().getUid())
                .eq("project_name", fileDeleteRequest.getPath()));
        deleteAll(path);
        return JsonData.buildSuccess("删除成功");
    }

    /**
     * 重命名文件/目录
     * @param fileRenameRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData rename(FileRenameRequest fileRenameRequest) {
        String oldPath = fileRenameRequest.getOldPath(), newPath = fileRenameRequest.getNewPath();
        File oldFile = new File(fileMappingUtil.mapping(oldPath));
        File newFile = new File(fileMappingUtil.mapping(newPath));
        if (!oldFile.exists() || newFile.exists()) {
            throw new BizException(BizCodeEnum.FILE_ERROR_OCCURRED);
        }
        if (oldFile.renameTo(newFile)) {
            //如果重命名的是根目录，那么数据库的项目名也需要更改
            if (fileRenameRequest.getIsRoot()) {
                ProjectDO projectDO = projectMapper.selectOne(new QueryWrapper<ProjectDO>().eq("uid", LoginInterceptor.THREAD_LOCAL.get().getUid())
                        .eq("project_name", oldPath));
                projectDO.setProjectName(newPath);
                projectMapper.updateById(projectDO);
            }
            return JsonData.buildSuccess();
        }
        return JsonData.buildError("重命名失败");
    }

    private void deleteAll(String filePath) {
        File file = new File(filePath);
        if (file.isFile()) {
            file.delete();
        }
        if (file.isDirectory()) {
            File[] fileAll = file.listFiles();
            if (fileAll == null) {
                return;
            }
            if(fileAll.length != 0) {
                for (File part : fileAll) {
                    deleteAll(part.getAbsolutePath());
                }
            } else {
                file.delete();
            }
            file.delete();
        }
    }
}
